#include <sys/socket.h>
#include <signal.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <assert.h>
#include "Facade.h"
#include "Threads.h"
#include "NetUtils.h"
#include "HttpHandler.h"
#include "SockHandler.h"
#include "aclHandler.h"
#include "Debug.h"
#include "Helper.h"

int w_sock_fd = 0;
void *zexmon_command_waiter(void *arg);

static void usage(void) {
    printf("-p <num>      TCP port number to listen on (default: 11211)\n"
           "-w <num>      zexmon port to listen on (default: 0,off)\n"
           "-s <file>     unix socket path to listen on (disables network support)\n"
           "-a <mask>     access mask for unix socket, in octal (default 0700)\n"
           "-l <ip_addr>  interface to listen on, default is INDRR_ANY\n"
           "-c <file>     configure file, default is ./helper.conf\n"
           "-m <num>      max data size, default is 1024*1024\n"
           "-f <file>     acl file, default NULL\n"
           "-o <num>      max simultaneous connections, default is 1024\n"
           "-h            print this help and exit\n"
	   "-t <num>      number of threads to use, default 4\n");
    return;
}


static void sig_handler(const int sig) {
    printf("SIGINT handled.\n");
    exit(EXIT_SUCCESS);
}

int main (int argc, char **argv) {
    int c;
 //   static int u_socket_count = 0;
    /* handle SIGINT */
    signal(SIGINT, sig_handler);
    /* init pR->GetSettings()->*/
    Facade *pR = Facade::Instance();
    string helper  = "./helper.conf";
    string aclConfig;
    int wport = 0;
    /* set stderr non-buffering (for running under, say, daemontools) */
    setbuf(stderr, NULL);
    /* process arguments */
    while ((c = getopt(argc, argv, "a:bp:s:U:m:Mc:khirvdl:u:P:f:s:n:t:o:w:D:L")) != -1) {
        switch (c) {
        case 'a':
            /* access for unix domain socket, as octal mask (like chmod)*/
            pR->GetSettings()->access= strtol(optarg,NULL,8);
            break;
        case 'w':
            wport = atoi(optarg);
            break;
        case 'p':
            pR->GetSettings()->port = atoi(optarg);
            break;
        case 's':
            pR->GetSettings()->socketpath = optarg;
            break;
        case 'f':
            DEBUG2("acl file = ", optarg);
            aclConfig = optarg;
            break;
        case 'm':
            pR->GetSettings()->maxbytes = ((size_t)atoi(optarg)) * 1024 * 1024;
            break;
        case 'o':
            pR->GetSettings()->maxconns = atoi(optarg);
            break;
        case 'c':
            DEBUG2("helper configure = ", optarg);
            helper = optarg;
            break;
        case 'h':
            usage();
            exit(EXIT_SUCCESS);
        case 'l':
            pR->GetSettings()->inter= strdup(optarg);
            break;
        case 't':
            pR->GetSettings()->num_threads = atoi(optarg);
            if (pR->GetSettings()->num_threads == 0) {
                fprintf(stderr, "Number of threads must be greater than 0\n");
                return 1;
            }
            break;
        default:
            fprintf(stderr, "Illegal argument \"%c\"\n", c);
            return 1;
        }
    }

    Helper *hp =  Helper::Instance(helper.c_str());
    if(hp == NULL)
    {
          ERRO <<"Can't Helper::Instance"<<endl;
          exit(0);
    }    

    if(pR->GetSettings()->num_threads <=1 && hp->getThreads() > 1 )
          pR->GetSettings()->num_threads =  hp->getThreads();
    if(pR->GetSettings()->num_threads < 1)
    {
          ERRO <<"thread num  == 0!"<<endl;
          exit(0);
    }    

    if(wport <= 0)
	    wport = hp->getWPort();

    if(pR->GetSettings()->port <= 0) 
	    pR->GetSettings()->port = hp->getPort();

    DEBUG4("wport =", wport, "; port", pR->GetSettings()->port);
    if(wport > 0)
    {
	    pthread_t       exmonthread;
	    pthread_attr_t  attr;
	    int             ret;
	    pthread_attr_init(&attr);
	    if((ret = pthread_create(&exmonthread, &attr, zexmon_command_waiter , &wport)) != 0)
	    {
		    fprintf(stderr, "Can't create thread: %s\n", strerror(ret));
		    exit(0);
	    }
    }
    if(aclConfig.length() < 1)
	    aclConfig = hp->getIPAcl();

    DEBUG2("use acl = ", aclConfig.c_str());
    aclHandler *pAcl = aclHandler::Instance(aclConfig.c_str());
    if(pAcl != NULL)
	    pAcl->Print();
    else
	    ERRO<<"No ACL to use!  "<<endl;

    Handler *pHandler = new SocketHandler();
    DEBUG1("Start ... ...");
    cout<<"start ... ... " << endl;
    pR->Start(pHandler);

    // never go here!!!!!!!
    delete pHandler;
    hp->Release(true );
    return 0;
}


void *zexmon_command_waiter(void *arg)
{
        int new_fd;
        int *port = (int *)arg;
        DEBUG2("zexmon port = ", *port);
        struct sockaddr_in my_addr;
        if((w_sock_fd = socket(AF_INET,SOCK_STREAM,0))==-1)
        {
                perror("socket");
                exit(1);
        }
        my_addr.sin_family = AF_INET;
        my_addr.sin_port = htons( *port );
        my_addr.sin_addr.s_addr = INADDR_ANY;
        bzero(&(my_addr.sin_zero),8);
        int flags = 1;
        setsockopt(w_sock_fd ,SOL_SOCKET ,SO_REUSEADDR, &flags, sizeof(int));
        long lft = time(NULL);
        while(bind(w_sock_fd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr))==-1)
        {
                usleep(10000);
                perror("bind");
                if(time(NULL) > lft + 120)
                        exit(1);
        }
        struct sockaddr_in their_addr;
        if(listen(w_sock_fd,1024)==-1) {
                perror("listen");
                exit(1);
        }
        while(1) {
                socklen_t sin_size = sizeof(struct sockaddr_in);
                if((new_fd = accept(w_sock_fd,(struct sockaddr *) &their_addr, &sin_size))==-1)
                {
                        perror("accept");
                        continue;
                }
                int numbytes = 0;
                char buff[1024];
                if((numbytes = read(new_fd, buff, 1024 )) != -1)
                {
                        buff[numbytes]= '\0';
                        DEBUG2("Input command: ", buff);
                        if(strstr(buff,"ruok") != 0){
                                numbytes = sprintf(buff,"imok");
                                DEBUG2("Ruok command ", "Done");
                        }else if(strstr(buff,"stat") != 0){
                                numbytes = sprintf(buff,"stat is ok");
                                DEBUG2("Stat command ", "Done");
                        }else if(strstr(buff,"kill") != 0){

                                DEBUG2("kill command ", "exit");
                                close(new_fd);
                                close(w_sock_fd);
                                exit(0);
                        }
                        if(send(new_fd,buff, numbytes, 0)==-1)
                                perror("send");
                }
                close(new_fd);
        }
        close(w_sock_fd);
        w_sock_fd = 0;
        return NULL;
}

